import ij.ImagePlus;
import ij.plugin.PlugIn;
import ij.process.ColorProcessor;
import ij.process.ImageProcessor;


import java.awt.Color;


import java.awt.Font;

import java.awt.Polygon;

import java.util.Locale;
import java.util.Random; 



import pluginUtils.Circle;
import pluginUtils.Complexe; 
import pluginUtils.MobiusTransform;


public class IndraGrandMap229 extends ImagePlus implements PlugIn{
	{Locale.setDefault(Locale.US); }
	
	private String[] palette={"Yellow","Red","Blue","Green","White","NavyBlue","Turquoise","Orange"};
	String letter[]={"a","B","A","b"};
	private ImageProcessor primeIP; 
	private int X=2000,Y=2000; // image size
	
	private Random generator=new Random(); // One always needs some randomness
	private Complexe oldPoint=null,newPoint; 
	// For the scaling of the image
	private double x0=-1,x1=1,y0=-1,y1=1;
	private double acoef,ccoef,vcoef,wcoef; 
	
	private static MobiusTransform[] ordered=new MobiusTransform[4];
	private static Color paleCol[]={Color.RED,Color.BLUE,Color.YELLOW,Color.GREEN,Color.BLACK,Color.CYAN,Color.DARK_GRAY,
        Color.MAGENTA,Color.ORANGE};
	
	public void setCoefs(){
		acoef=X/(x1-x0); 
		vcoef=Y/(y1-y0); 
		ccoef=-acoef*x0; 
		wcoef=-vcoef*y0; 
	}
	 
	
	
	public void setBounds(double xl,double xu,double yl,double yu){
		x0=xl; 
		x1=xu;
		y0=yl;
		y1=yu; 
	}
	
	
	private static double altitude=0; 
	
	
	private static Complexe ta,tb;
	private static MobiusTransform Ta,Tb,TA,TB; 
	
	
	
	private static void init(){
	ta=Complexe.createComplexeFromRI(1.91,0.05); 
	tb=Complexe.createComplexeFromRI(1.91,0.05); 
	Complexe ta2=Complexe.mul(ta,ta); 
	Complexe tb2=Complexe.mul(tb,tb); 
	Complexe delta=Complexe.mul(ta2,tb2); 
	Complexe qta2=Complexe.mulbyDouble(ta2,4);
	Complexe qtb2=Complexe.mulbyDouble(tb2,4);
	
	delta=Complexe.sub(delta,qta2); 
	delta=Complexe.sub(delta,qtb2);
	delta=Complexe.sqrt(delta);
	System.out.println("delta : "+delta); 
	
	Complexe tatb=Complexe.mul(ta,tb);
	Complexe tab=Complexe.sub(tatb,delta);
	tab=Complexe.mulbyDouble(tab,0.5);
	System.out.println("tab :"+tab);
	//OK (formule) calcul ?
	
	Complexe z0=Complexe.sub(tab,Complexe.createComplexeFromRI(2,0));
	z0=Complexe.mul(z0,tb);
	
	
	Complexe provz=Complexe.mul(tb,tab);
	Complexe t2=Complexe.mul(ta,Complexe.createComplexeFromRI(-2,0));
	Complexe t3=Complexe.mul(tab,Complexe.createComplexeFromRI(0,2));
	Complexe denomz=Complexe.sum(provz,t2);
	denomz=Complexe.sum(denomz,t3);
	
	z0=Complexe.div(z0,denomz);
	System.out.println("z0 :"+z0);
	
	Complexe a1=Complexe.mulbyDouble(ta,0.5);
	
	Complexe tatab=Complexe.mul(ta,tab);
	Complexe dtb=Complexe.mulbyDouble(tb,-2);
	Complexe prov1=Complexe.sum(tatab,dtb); 
	Complexe qi=Complexe.createComplexeFromRI(0,4); 
	Complexe numer1=Complexe.sum(prov1,qi);
	
	Complexe dtab=Complexe.mulbyDouble(tab,2);
	dtab=Complexe.sum(dtab,Complexe.createComplexeFromRI(4,0));
	dtab=Complexe.mul(dtab,z0); 
	
	Complexe b1=Complexe.div(numer1,dtab);
	
	Complexe numer2=Complexe.sum(prov1,Complexe.createComplexeFromRI(0,-4));
	numer2=Complexe.mul(numer2,z0);
	
	
	Complexe denom2=Complexe.sum(Complexe.mulbyDouble(tab,2),Complexe.createComplexeFromRI(-4,0));
	
	Complexe c1=Complexe.div(numer2,denom2);
	
	Ta=new MobiusTransform(a1,b1,c1,a1); 
	TA=Ta.inverse(); 
	
	Complexe tbs2=Complexe.mulbyDouble(tb,0.5);
	Complexe a2=Complexe.sum(tbs2,Complexe.createComplexeFromRI(0,-1));
	Complexe d2=Complexe.sum(tbs2,Complexe.createComplexeFromRI(0,1));
	
	Tb=new MobiusTransform(a2,tbs2,tbs2,d2); 
	TB=Tb.inverse(); 
	System.out.println(Ta.trace()+"\n"+TA.trace()+"\n"+Tb.trace()+"\n"+TB.trace());
	System.out.println("Ta det : "+Ta.determinant());
	System.out.println("TA det : "+TA.determinant());
	System.out.println("Tb det : "+Tb.determinant());
	System.out.println("TB det : "+TB.determinant());
	
	MobiusTransform abAB=Ta.mul(Tb.mul(TA.mul(TB))); 
	System.out.println("abAB");
	System.out.println(abAB);
	System.out.println(Ta);
	ordered[0]=Ta; 
	ordered[1]=TB;
	ordered[2]=TA;
	ordered[3]=Tb;
	}
	
	
	
	
	
	
	
	private void drawCircle(Circle c,int level){
		double xCenter=c.getCenter().getReal(); 
		double yCenter=c.getCenter().getIm(); 
		
		
		int intX=(int)(xCenter*acoef+ccoef);
		int intY=(int)(yCenter*vcoef+wcoef);
		int intRx=(int)(c.getRadius()*acoef);
		int intRy=(int)(c.getRadius()*vcoef);
		
		primeIP.drawOval(intX-intRx,intY-intRy,intRx*2,intRy*2);
	//	String s="sphere{\n<"+xCenter+","+yCenter+","+altitude+">,";
	//	s+=c.getRadius(); 
	//	s+=" texture{pigment {color "+palette[level]+" } finish {reflection 0.5 specular 0.7}}}"; 
	//	System.out.println(s); 
		altitude*=0.5;
		
	}
	
	private void drawPoint(Complexe c){
		double xCenter=c.getReal(); 
		double yCenter=c.getIm(); 
		int intX=(int)(xCenter*acoef+ccoef);
		int intY=(int)(yCenter*vcoef+wcoef);
		primeIP.fillOval(intX-10,intY-10,20,20);
	}
	
	
	private void drawLine(Complexe origin,Complexe destination){
		if(Complexe.distance(origin,destination)<1)
		drawLine(origin.getReal(),origin.getIm(),destination.getReal(),destination.getIm()); 
		
	}
	
	
	
	private void drawLine(double xd,double yd,double xf,double yf){
		int XD=(int)(xd*acoef+ccoef); 
		int YD=(int)(yd*vcoef+wcoef); 
		int XF=(int)(xf*acoef+ccoef); 
		int YF=(int)(yf*vcoef+wcoef);
		primeIP.drawLine(XD,YD,XF,YF); 
	}
	
	// O -> transa
	// 1 -> transA
	// 2 -> transb
	// 3 -> transB
	private void buildSuccessors(Circle c,int level,int exclude){
		 if(level==0) drawCircle(c,level);
		if(level==0)  {return;}
		if(exclude!=0) buildSuccessors(TA.circleImage(c),level-1,1); 
		if(exclude!=1) buildSuccessors(Ta.circleImage(c),level-1,0); 
		if(exclude!=2) buildSuccessors(TB.circleImage(c),level-1,3); 
		if(exclude!=3) buildSuccessors(Tb.circleImage(c),level-1,2); 
	}
	
	private void ExploreTree(Complexe c,int genIndex,int level,String s){
		//System.out.println(s);
		for(int k=genIndex-1;k<=genIndex+1;k++){
			int i=k; 
			if(k==-1)i=3; 
			if(i==4) i=0; 
			if(level==0){
				if(oldPoint!=null)
					drawLine(oldPoint,c); 
				oldPoint=new Complexe(c);
			}
			else {
				
				ExploreTree(ordered[i].pointImage(c),i,level-1,s+letter[i]);
			}
		}
		
	}
	
	
	
	public void run(String arg) {
		primeIP = new ColorProcessor(X,Y);
		
		
		setBounds(-10,10,-10,10); 
		setCoefs(); 
		init();
		Polygon background=new Polygon(new int[]{0,0,X,Y},new int[]{0,X,Y,0},4);
		primeIP.setColor(Color.WHITE);
		primeIP.fillPolygon(background);
		primeIP.setFont(new Font(null,Font.ITALIC,60));
		primeIP.setColor(Color.BLACK);
		primeIP.setLineWidth(5); 
		
		
		
		drawLine(x0,0,x1,0);
		drawLine(0,y0,0,y1);
	
		setProcessor("Verifying", primeIP);
		/***************** Drawing instructions come here *****************************************/
		
		for(int k=0;k<1;k++) {
			primeIP.setColor(Color.BLACK);
			drawPoint(ordered[k].getFixPoint0());
			primeIP.setColor(paleCol[k]);
			ExploreTree(ordered[(k+2)%4].getFixPoint0(),k,10,letter[k]); 
		}
		
		/*
		Complexe center=Complexe.createComplexeFromRI(0,0); 
		IndraCircle seed=new IndraCircle(center,2);
		
		drawCircle(seed,5);
		primeIP.setColor(Color.RED);
		buildSuccessors(seed, 5, -1);
		*/
		
		
		
		
		/***************** End of drawing instructions ********************************************/
		show();
		System.out.println("done "); 
	}	
}
	